package org.java.auth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.authserver.AuthorizationServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import java.security.KeyPair;

@Configuration
@EnableAuthorizationServer
@EnableConfigurationProperties(AuthorizationServerProperties.class)// 激活某个配置属性的应用
public class OAuthConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private PasswordEncoder passwordEncoder;

    // 要在SecurityConfig里面暴露AuthenticationManager
    @Autowired
    private AuthenticationManager authenticationManager;

    // 使用@EnableConfigurationProperties激活
    @Autowired
    private AuthorizationServerProperties authorizationServerProperties;

    //配置客户端信息
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                //在内存中存储客户端信息
                .inMemory()
                //客户端的id，用于识别客户端，跟网关上面的 客户端名称一致
                .withClient("api-gateway")
                //客户端授权的时候，同样要求客户端提供一个密码，要使用PasswordEncoder进行加密
                .secret(passwordEncoder.encode("1234"))
                //授权范围
                .scopes("all")
                // 客户端支持的类型
                .authorizedGrantTypes(
                        // 获取CODE
                        "authorization_code",
                        // 基于密码的方式直接访问、授权
                        "password",
                        // 基于Token方式访问，基本上都使用这种
                        "token",
                        // 允许自动确认，并且要严格授权
                        "implicit")
                //授权之后，重定向到客户端的什么位置，重定向会携带code和state上去
                .redirectUris(
                        "/login/oauth2/code/gateway",
                        "http://127.0.0.1:30001/login/oauth2/code/gateway",
                        "http://127.0.0.1:8080/login/oauth2/code/gateway",
                        "http://api-gateway:8080/login/oauth2/code/gateway",
                        "http://api.fkjava.org/login/oauth2/code/gateway",
                        "https://api.fkjava.org/login/oauth2/code/gateway",
                        "http://127.0.0.1:9090/login/oauth2/code/gateway",
                        "http://0.0.0.0:9090/login/oauth2/code/gateway"
                )
                .autoApprove(true);
    }

    //授权服务 比如表单授权
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //基于表单的方式进行授权
        security.allowFormAuthenticationForClients();
        security.passwordEncoder(passwordEncoder);
    }

    //配置JWT
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                // 在刷新令牌的时候，是否重用之前的刷新的刷新令牌。
                // 令牌有3个：临时令牌（code），访问令牌（accessToken），刷新令牌（refreshToken）
                .reuseRefreshTokens(true)
                //认证管理器
                .authenticationManager(authenticationManager)
                //令牌存储器
                .tokenStore(tokenStore())
                //访问令牌转换器
                .accessTokenConverter(tokenConverter());
    }


    // 在配置类里面，使用了@Bean注解的方法，只会被调用一次。即使有多次调用，也会被代理掉。
    @Bean
    JwtTokenStore tokenStore() {
        return new JwtTokenStore(tokenConverter());
    }
    @Bean
     JwtAccessTokenConverter tokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

        KeyStoreKeyFactory keyStoreKeyFactory
                = new KeyStoreKeyFactory(
                // ClassPathResource类相当于就是 classpath:/
                new ClassPathResource(authorizationServerProperties.getJwt().getKeyStore()),
                authorizationServerProperties.getJwt().getKeyStorePassword().toCharArray());
        KeyPair keyPair = keyStoreKeyFactory
                .getKeyPair(authorizationServerProperties.getJwt().getKeyAlias());

        converter.setKeyPair(keyPair);
        return converter;
    }
}
